/*

Miranda IM: the free IM client for Microsoft* Windows*

Copyright 2000-2007 Miranda ICQ/IM project, 
all portions of this codebase are copyrighted to the people 
listed in contributors.txt.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
aLONG with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/

/**
 * Local Includes:
 **/
#include "commonheaders.h"
#include "version.h"
#include "ctrl.h"
#include "Reminder.h"
#include "svcExImport.h"

#include "menuitems.h"
#include "options.h"
#include "ExtraServices.h"

#include "ctrlButton.h"
#include "dlgPropsheet.h"
#include "dlgAnniversaryList.h"

/**
 * Miranda Includes:
 **/
#include "m_updater.h"

// local:

static PLUGININFOEX pluginInfo = {
	sizeof(PLUGININFOEX),
	__PLUGIN_DISPLAY_NAME,
	__VERSION_DWORD,
	__DESC,
	__AUTHOR,
	__AUTHOREMAIL,
	__COPYRIGHT,
	__AUTHORWEB,      
	UNICODE_AWARE,
	DEFMOD_UIUSERINFO,
	MIID_UIUSERINFOEX
};

static HANDLE	ghModulesLoadedHook		= NULL;
static HANDLE	ghTopToolBarModuleLoaded = NULL;

INT		ContactInfo_OnSettingChanged(WPARAM wParam, LPARAM lParam);
VOID	ContactInfo_LoadModule();

/*
============================================================================================
	event hooks
============================================================================================
*/

/**
 * name:	OnContactSettingChanged
 * desc:	is called if a contact setting has changed
 * param:	wParam	- handle to contact
 *			lParam	- DBCONTACTWRITESETTING structure to identify the changed setting
 * return:	must always return 0, to give other plugins the chance to handle this notification
 **/
static INT OnContactSettingChanged(WPARAM wParam, LPARAM lParam)
{
	ContactInfo_OnSettingChanged(wParam, lParam);
	return 0;
}

/**
 * name:	OnMetaSubContactChanged()
 * desc:	copy all userinfo from all the subcontacts to the meta contact, beginning with the default one
 * param	wParam	-	handle to the metacontact
 *			lParam	-	NULL
 * return:	0
 **/
INT OnMetaSubContactChanged(HANDLE hMeta, LPARAM lParam)
{
	INT i;
	HANDLE hSubContact, hDefSubContact;

	if(!DBGetByte(SET_META_COPYDATA, 0))
		return 0;

	if(hDefSubContact = (HANDLE)CallService(MS_MC_GETDEFAULTCONTACT, (WPARAM)hMeta, NULL)) {
		DBCopyModule(hDefSubContact, USERINFO, hMeta, META_PROTO, TRUE);
		DBCopyModule(hDefSubContact, MOD_MBIRTHDAY, hMeta, MOD_MBIRTHDAY, TRUE);

		// copy the missing settings from the other subcontacts
		INT numSubs = CallService(MS_MC_GETNUMCONTACTS, (WPARAM)hMeta, NULL);
		for(i = 0; i < numSubs; i++) {
			if((hSubContact = (HANDLE)CallService(MS_MC_GETSUBCONTACT, (WPARAM)hMeta, i)) && hSubContact != hDefSubContact) {
				DBCopyModule(hDefSubContact, USERINFO, hMeta, META_PROTO, FALSE);
				DBCopyModule(hDefSubContact, MOD_MBIRTHDAY, hMeta, MOD_MBIRTHDAY, FALSE);
			}
		}
	}
	return 0;
}

/**
 * name:	OnTopToolBarModuleLoaded()
 * desc:	is called by Miranda just after loading the TopToolBar module
 *
 * return:	0
 **/
static INT OnTopToolBarModuleLoaded(WPARAM wParam, LPARAM lParam) 
{
	NAnniversaryList::OnTopToolBarModuleLoaded();
	NReminder::OnTopToolBarModuleLoaded();
	UnhookEvent(ghTopToolBarModuleLoaded);
	return 0;
}

/**
 * name:	OnContactAdded()
 * desc:	Is called by Miranda just after adding a contact to database.
 *			It stores the date, the contact was added in database for later use.
 * return:	0
 **/
static INT OnContactAdded(WPARAM wParam, LPARAM lParam) 
{
	MTime mt;
	
	mt.GetLocalTime();
	mt.DBWriteStamp((HANDLE)wParam, USERINFO, SET_CONTACT_ADDEDTIME);
	return 0;
}

/**
 * name:	OnModulesLoaded()
 * desc:	is called by Miranda just after loading all system modules
 *
 * return:	0
 **/
static INT OnModulesLoaded(WPARAM wParam, LPARAM lParam) 
{
	UnhookEvent(ghModulesLoadedHook);

	myGlobals.IsCListNicer = ServiceExists("CListFrame/SetSkinnedFrame");
	myGlobals.HaveCListExtraIcons = ServiceExists(MS_CLIST_EXTRA_SET_ICON);

	// some stupid test, whether we have META_PROTO plugin or not
	if(myGlobals.HaveMetaContacts = ServiceExists(MS_MC_GETMOSTONLINECONTACT) && ServiceExists(MS_MC_GETNUMCONTACTS))
		HookEvent(ME_MC_SUBCONTACTSCHANGED, (MIRSERVICE)OnMetaSubContactChanged);
	// create hooks
	HookEvent(ME_DB_CONTACT_SETTINGCHANGED, OnContactSettingChanged);
	HookEvent(ME_DB_CONTACT_ADDED, OnContactAdded);
	
	// load icon library
	NIcoLib::LoadModule();
	// options
	NOptions::LoadModule();
	// create services to receive string lists of languages and timezones
	Constants_LoadModule();
	// load module to remind user about birthday and a anniversary
	NReminder::LoadModule();
	// load extended intagration services
	NServices::NSendRecvEMail::OnModulesLoaded();
	NServices::NViewHomepage::OnModulesLoaded();
	NServices::NSendSMS::OnModulesLoaded();
	NServices::NGender::OnModulesLoaded();
	
	// NServices::NAvatar::OnModulesLoaded();
	
	// build contact's menuitems
	MenuItems::RebuildContact();
	MenuItems::RebuildMain();
	MenuItems::RebuildGroup();

	// finally register for updater
	CallService(MS_UPDATE_REGISTERFL, (WPARAM)__UPDATER_DOWNLOAD_ID, (LPARAM)&pluginInfo);
	// install known modules strings to database
	DBWriteContactSettingString(NULL, "KnownModules", MODULELONGNAME, "UserInfo,UserInfoEx,UserInfoExW");
	return 0;
}

/*
============================================================================================
	plugin interface & DllEntrypoint
============================================================================================
*/

static PLUGININFO oldPluginInfo = {
	sizeof(PLUGININFO),
	pluginInfo.shortName,
	pluginInfo.version,
	pluginInfo.description,
	pluginInfo.author,
	pluginInfo.authorEmail,
	pluginInfo.copyright,
	pluginInfo.homepage,
	pluginInfo.flags,
	pluginInfo.replacesDefaultModule
};

/**
 * name:	MirandaPluginInfo()
 * desc:	is called by Miranda to get some information about this plugin
 *
 * return:	pointer to pluginInfo struct
 **/
extern "C" __declspec(dllexport) PLUGININFO *MirandaPluginInfo(DWORD mirandaVersion)
{
	// check whether database supports utf8 strings
	myGlobals.mirandaVersion = mirandaVersion;

	if(mirandaVersion < PLUGIN_MAKE_VERSION(0,6,0,0))
		return NULL;
	return &oldPluginInfo;
}

/**
 * name:	MirandaPluginInfo()
 * desc:	is called by Miranda to get some information about this plugin
 *
 * return:	pointer to pluginInfo struct
 **/
extern "C" __declspec(dllexport) PLUGININFOEX *MirandaPluginInfoEx(DWORD mirandaVersion) 
{
	myGlobals.mirandaVersion = mirandaVersion;
	return &pluginInfo;
}

/**
 * name:	MirandaPluginInterfaces()
 * desc:	return the provided interfaces
 *
 * return:	pointer to pluginInfo struct
 **/
extern "C" __declspec(dllexport) const MUUID *MirandaPluginInterfaces()
{
	static const MUUID interfaces[] = {
		MIID_UIUSERINFOEX,	// this is just me
		MIID_UIUSERINFO,	// replace the default userinfo module
		MIID_CONTACTINFO,	// indicate, that MS_CONTACT_GETCONTACTINFO service is provided 
		MIID_REMINDER,		// indicate an Reminder of being provided
		MIID_SREMAIL,		// Send/Receive E-Mail service is provided
		MIID_LAST
	};
	return interfaces;
}

/**
 * name:	Unload()
 * desc:	is called by Miranda just to make it possible to unload some memory, ...
 *
 * return:	0
 **/
extern "C" INT __declspec(dllexport) Unload(VOID)
{
	NContactDetailsPS::UnLoadModule();
	NReminder::UnloadModule();

	// uninitialize classes
	CbEx_UnLoadModule();
	NCtrl::NButton::UnloadModule();
	NIcoLib::CloseLibrary();

	Constants_UnloadModule();
	return 0;
}

/**
 * name:	Load()
 * desc:	is called by Miranda to get the plugin initialized
 *
 * return:	0
 **/
extern "C" INT __declspec(dllexport) Load(PLUGINLINK *link)
{
	INITCOMMONCONTROLSEX ccEx;

	pluginLink = link;

	ZeroMemory(&myGlobals, sizeof(MGLOBAL));

	// load message box service
	CreateServiceFunction(MS_MSGBOX, MsgBoxService);

#ifdef _UNICODE
	CHAR szVer[MAX_PATH] = { 0 };
	
	if((GetVersion() & 0x80000000) != 0) {
		MsgErr(NULL, _T("Sorry, but UserInfoExW needs an Unicode supporting OS!"));
		return 1;
	}
	CallService(MS_SYSTEM_GETVERSIONTEXT, MAX_PATH, (LPARAM)szVer);
	if((strstr(szVer, "Unicode") == NULL)) {
		MsgErr(NULL, _T("UserInfoExW does not work with Miranda IM (ANSI)\nTry UserInfoEx instead!"));
		return 1;
	}
#endif
	// get the codepage of miranda's langpack to make MultiByteToWideChar(..) work correctly
	if((myGlobals.CodePage = CallService(MS_LANGPACK_GETCODEPAGE, 0, 0)) == CALLSERVICE_NOTFOUND)
		myGlobals.CodePage = CP_ACP;
	// init common controls
	ccEx.dwSize = sizeof(ccEx);
	ccEx.dwICC = ICC_WIN95_CLASSES|ICC_DATE_CLASSES;
	InitCommonControlsEx(&ccEx);
	// init miranda's memory interface
	mmi.cbSize = sizeof(MM_INTERFACE);
	CallService(MS_SYSTEM_GET_MMI, NULL, (LPARAM)&mmi);
	// replace default GetContactInfo service
	ContactInfo_LoadModule();
	// load my button class
	NCtrl::NButton::LoadModule();
	// load module for email and phone combobox
	CbEx_LoadModule();
	// load module for handling contact times
	UserTime_LoadModule();
	// load vcard module
	NExImport::LoadModule();
	// load the UserInfoPropertySheet module
	NContactDetailsPS::LoadModule();
	NAnniversaryList::LoadModule();
	// prevent standard send/receive email service from loading
	NServices::NSendRecvEMail::LoadModule();

	// Now the module is loaded! Start initializing certain things
	ghModulesLoadedHook = HookEvent(ME_SYSTEM_MODULESLOADED, OnModulesLoaded);
	ghTopToolBarModuleLoaded = HookEvent(ME_TTB_MODULELOADED, OnTopToolBarModuleLoaded);
	return 0;
}

/**
 * name:	DllMain()
 * desc:	windows needs it for loading
 *
 * return:	0
 **/
BOOL WINAPI DllMain(HINSTANCE hinst, DWORD fdwReason, LPVOID lpvReserved)
{
	switch(fdwReason) {
		case DLL_PROCESS_ATTACH:
			ghInst = hinst;
			break;
	}
	return TRUE;
}

